/****************************************************************
 * Homework 6 'Scheduler' class implementation file
 * Author: Duncan Buell
 * Modified by: Andrew Shore, Wes Ring, Colin Edwards, JC Anderson
 * Date last modified: 1 April 2010
 *
 * This class manages a priority queue of process blocks for
 * simulating process management in an operating system. It is 
 * slightly more complex than Homework 5 since process are
 * loaded into a pending queue first and then put it the ready
 * queue when they are ready to start.
 * 
 * 
**/

#include "Scheduler.h"

static const char TAG[] = "Scheduler: ";

/****************************************************************
 * Constructor.
**/
Scheduler::Scheduler()
{
}

/****************************************************************
 * Destructor.
**/
Scheduler::~Scheduler()
{
}


/*******************************************************************************
 * Function to create a process using the given parameters and add it to the
 * pending priority queue.
 * Author: Andrew Shore, Wes Ring, JC Anderson, Colin Edwards
 * Date Last Modified: 1 April 2010
 *
 * This function creates a new process, sets the class variables of that
 * process by passing the parameters to the mutator functions and then uses
 * the STL priority queue function push() to add it to the pending queue.  
 * No error checking is done in this function, the parameters are assumed to 
 * be correct when passed in by the main function.
 *
 * Parameters:
 *   long pid - used to set process id
 *   long startTime - used to set start time
 *   long execTime - used to set execution time
 *
 * Output: none
 *
 * Returns: none
*******************************************************************************/
void Scheduler::addPending(long pid, long startTime, long execTime){
  Process newProcess;
  newProcess.setPid(pid);
  newProcess.setStartTime(startTime);
  newProcess.setExecTime(execTime);

  thePendingQ.push(newProcess);
 
}//void Scheduler::addPending(long pid, long startTime, long execTime)

/*******************************************************************************
 * Function to check if both priority queues are empty.
 * Author: Andrew Shore, Wes Ring, JC Anderson, Colin Edwards
 * Date Last Modified: 1 April 2010
 *
 * Simply calls the STL priority queue function empty() to check if both the
 * 'theReadyQ' and the 'thePendingQ' are empty. 
 * 
 *
 * Parameters: none
 *
 * Output: none
 *
 * Returns: True if both the pending and ready queue are empty, false otherwise
*******************************************************************************/
bool Scheduler::empty(){
  if( theReadyQ.empty() && thePendingQ.empty() ){
    return true;
  }else{
    return false;
  }
}//bool Scheduler::empty()

/*******************************************************************************
 * Function to 'pop' off(remove) top element in the ready priority queue.
 * Author: Andrew Shore, Wes Ring, JC Anderson, Colin Edwards
 * Date Last Modified: 1 April 2010
 *
 * Function pops off the top element of the queue(the element with the smallest
 * finish time) provided the priority queue is non-empty.
 *
 * Parameters: none
 * 
 * Output: none
 *
 * Returns: none
*******************************************************************************/
void Scheduler::readyPop(){
  if( !theReadyQ.empty() ){
    theReadyQ.pop();
  }
}//void Scheduler::readyPop()

/*******************************************************************************
 * Function to 'pop' off(remove) top element in the pending queue
 * Authors: Andrew Shore, JC Anderson, Wes Ring, Colin Edwards
 * Date Last Modified: 1 April 2010
 *
 * Function pops off the top element of the pending queue(the element with the 
 * smallest start time) provided the priority queue is non-empty.
 * 
 * Parameters: none
 *
 * Output: none
 *
 * Returns: none 
*******************************************************************************/
void Scheduler::pendingPop(){
  if( !thePendingQ.empty() ){
    thePendingQ.pop();
  }
}//void Scheduler::pendingPop()

/*******************************************************************************
 * Function to look at the top process of the ready queue but not remove it
 * Author: Andrew Shore, Wes Ring, JC Anderson, Colin Edwards
 * Date Last Modified: 1 April 2010
 *
 * If the priority queue is non-empty it returns the top process, otherwise 
 * it returns an unitialized proccess.
 *
 * Parameters: none
 *
 * Output: none
 *
 * Returns: A process with the smallest finish time
*******************************************************************************/
Process Scheduler::readyTop(){
  Process temp;
  if( theReadyQ.empty() ){
    return temp;
  }else{
    return theReadyQ.top();
  }
}//Process Scheduler::readyTop()

/*******************************************************************************
 * Function to look at the top process of the pending queue but not remove it
 * Authors: Andrew Shore, JC Anderson, Wes Ring, Colin Edwards
 * Date Last Modified: 1 April 2010
 *
 * If the priority queue is non-empty it returns the top process, otherwise it
 * returns an unitialized process.
 *
 * Parameters: none
 *
 * Output: none
 *
 * Returns: A process with the smallest start time
*******************************************************************************/
Process Scheduler::pendingTop(){
  Process temp;
  if( thePendingQ.empty() ){
    return temp;
  }else{
    return thePendingQ.top();
  }
}//Process Scheduler::pendingTop()

/*******************************************************************************
 * Function to 'dump' the pending queue and assign it to a string
 * Authors: Andrew Shore, JC Anderson, Wes Ring, Colin Edwards
 * Date Last Modified: 1 April 2010
 *
 * This function first creates a copy of the pending queue so we do not destroy
 * the orginal queue. Then the top element is added to the string using the 
 * toString function of the process class. The element is then popped off the 
 * queue. The function does this until there are no more elements in the queue
 * and then returns a string that is an ordered list(by startTime) of processes.
 *
 * Parameters: none
 * 
 * Output: log of what occurs
 *
 * Returns: string version of the pending queue
 */
string Scheduler::toStringDumpPendingQ(){
cout << TAG << " enter toStringDumpPendingQ\n";

  string s = "";
  Process temp;
  priority_queue<Process, vector<Process>, ComparisonStart>
                                       tempPQ = thePendingQ;  

  cout << TAG << " pending queue:\n";
  if ( tempPQ.empty() ){
    s += "empty";
  }else{
    while( !tempPQ.empty() ){
      temp = tempPQ.top();
      s += temp.toString() + "\n";
      tempPQ.pop();
    }//while( !tempPQ.empty() )
  }
  
  s += Utils::Format(TAG);
  s += " leaving toStringDumpPendingQ\n";
  return s;  

}//string Scheduler::toStringDumpPendingQ()

/*******************************************************************************
 * Function to 'dump' the ready queue and assign it to a string
 * Authors: Andrew Shore, JC Anderson, Wes Ring, Colin Edwards
 * Date Last Modified: 1 April 2010
 *
 * Function first creates a copy of the ready queue so we don't destroy the 
 * original queue. If the queue is empty the string "empty" is returned. If it
 * is not empty the top element of the queue is assigned to a string using the
 * toString function of the process class then the element is popped off. This
 * is repeated until the queue contains no elements. As it is in the current
 * implementation this function is only called when the theReadyQ is empty,
 * the normal non-empty dump is included for debugging purposes.
 *
 * Parameters: none
 *
 * Output: log of what occurs
 *
 * Returns: a string version of the ready queue
 */
string Scheduler::toStringDumpReadyQ(){
  cout << TAG << "enter toStringDumpReadyQ\n";
  
  cout << TAG << "ready queue:\n";

  priority_queue<Process, vector<Process>, ComparisonFinish>
                                       tempPQ = theReadyQ;
  string s;
  Process temp;

  if( tempPQ.empty() ){
    cout << TAG << "empty queue\n";
    
  }else{
    while( !tempPQ.empty() ){
      temp = tempPQ.top();
      s += temp.toString() + "\n";
      tempPQ.pop();
    }//while( !tempPQ.empty() )
    
  }
  
  cout << TAG << "leave toStringDumpReadyQ\n";

  return s;
}//string Scheduler::toStringDumpReadyQ()

/*****************************************************************************  
 * Function to put the processes in the input file into the pending queue
 * Authors: Andrew Shore, JC Anderson, Wes Ring, Colin Edwards
 * Date Last Modified: 1 April 2010
 *
 * This function reads in the pid, startTime, execTime from the input file and
 * then adds the procces to the queue using the function addPending. No error
 * checking is done in this function the file is assumed to be formatted
 * correctly and contains correct data.
 *
 * Parameters:
 *   inStream - file containing the list of process to be simulated, formatted
 *              'pid  startTime  execTime'
 *
 * Output: none
 *
 * Returns: none
 */
void Scheduler::loadPendingQ(ifstream& inStream){
  long pid;
  long startTime;
  long execTime;
  while( inStream >> pid ){ 
    inStream >> startTime;
    inStream >> execTime;
    addPending(pid, startTime, execTime);    
  }//while( inStream >> pid)


}//void Scheduler::loadPendingQ(ifstream& inStream)

/*******************************************************************************
 * Function runs a simulation of processes much like the main class did
 * in Assignment 5 only more complex.
 * Authors: Andrew Shore, JC Anderson, Wes Ring, Colin Edwards
 * Date Last Modified: 1 April 2010
 *
 * Parameters: 
 *   ifstream& - stream for the file containing the process data, it is 
 *               assumed that the number of timesteps is read in the main
 *               and the process are formatted 'pid startTime execTime'
 *   maxTimeStep - maximum number of times the simulation will run, read by
 *                 main from file
 *
 * This function first calls the loadPendingQ function that adds all the
 * process from the input file to the pending queue. Then both toStringDump
 * functions are called for each queue to display what proccess they have in 
 * them(note the theReadyQ should be empty). Then the simulation is run until
 * either the maxTimeStep is reached or we run out of processes. When the start
 * time of the top process in the pending queue matches the current time step
 * it is taken off the pending queue and put onto the ready queue. When the 
 * finish time of the top process in the ready queue matches the current time 
 * step it is removed. The information of processes being added and those 
 * finishing is displayed at each time step.
 * 
 * Output: log of what occurs
 *
 * Returns: none
*******************************************************************************/
void Scheduler::runSimulation(ifstream& inStream, long maxTimeStep){
  long currentTimeStep = 0;
  Process startProc;
  Process finishProc;

  cout << TAG << "entering runSimulation function\n";

  loadPendingQ(inStream);

  cout << toStringDumpPendingQ() << endl;
  cout << toStringDumpReadyQ() << endl;

  while( currentTimeStep != maxTimeStep){
    
    if( empty() ){
      cout << TAG << "no more processes, ending execution\n";
      break;
    }

    cout << TAG << "adding at time  " << currentTimeStep << endl;

    if(  pendingTop().getStartTime() == currentTimeStep){
      
      // loop moves pending process to ready queue when ready to start
      while( pendingTop().getStartTime() == currentTimeStep ){
        startProc = pendingTop();
        cout << "     "  <<  "adding     " << startProc.toString() << endl;
        theReadyQ.push(startProc);
        pendingPop();
        
      }//while( pendingTop().getStartTime() == currentTimeStep)
    }else if( thePendingQ.empty() ){
      cout << "     pending queue is empty\n";
    }
    else{
      cout << "     no processes added at time " << currentTimeStep << endl;
    }

    cout << TAG << "finishing at time " << currentTimeStep << endl;

    if(  readyTop().getFinishTime() == currentTimeStep){
     
      // loop removes process from ready queue when finished
      while( readyTop().getFinishTime() == currentTimeStep ){
        finishProc = readyTop();
        cout << "     " << "finishing  " << finishProc.toString() << endl;
        readyPop();
        
      }//while( readyTop().getFinishTime() == currentTimeStep)
    }else{
      cout << TAG << "no processes finishing at time " 
           << currentTimeStep << endl;
    }
    if( theReadyQ.empty() ){
      cout << "     ready queue is empty\n";
    }
    
    currentTimeStep++;     

  }//while( currentTimeStep != maxTimeStep)
  cout << TAG << "leaving runSimulation function\n";
}//void Scheduler::runSimulation(ifstream& inStream, long maxTimeStep)



